﻿using Abp.Domain.Uow;
using Abp.Runtime.Session;
using Abp.UI;
using Hicap.AuthorizationServer.Core.Common;
using Hicap.AuthorizationServer.Core.Models;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Serilog;
using System;
using System.Threading.Tasks;
using IUnitOfWork = Hicap.AuthorizationServer.Core.Common.EFCore.IUnitOfWork;

namespace Hicap.AuthorizationServer.Core.Managers
{
    public class UserManager : ManagerBase<User>, IUserManager
    {
        private readonly INotificationManager _notificationManager;
        private readonly IUserRoleRelatedManager _userRoleRelatedManager;
        private readonly IAbpSession _session;

        public UserManager(IUnitOfWork unitOfWork, IAbpSession session,
          INotificationManager notificationManager,
            IUserRoleRelatedManager userRoleRelatedManager

          ) : base(unitOfWork)
        {
            _notificationManager = notificationManager;

            _session = session;
            _userRoleRelatedManager = userRoleRelatedManager;

            QueryCondition.Add(x => x.IsDeleted == false);
            CreateActions.Add(x => x.CreatorUserId = session.UserId);
            CreateActions.Add(x => x.CreationTime = DateTime.Now);
            UpdateActions.Add(x => x.LastModificationTime = DateTime.Now);
            UpdateActions.Add(x => x.LastModifierUserId = session.UserId);
            DeleteActions.Add(x => x.DeletionTime = DateTime.Now);
            DeleteActions.Add(x => x.DeleterUserId = session.UserId);
            DeleteActions.Add(x => x.IsDeleted = true);

            CreateCheckActions.Add(x =>
            {
                if (x.Id == 0) return true;
                Log.Warning($"User CreateCheck Fail : {JsonConvert.SerializeObject(x)}");
                return false;
            });

            UpdateCheckActions.Add(x =>
            {
                if (x.Id > 0 && x.IsDeleted == false) return true;
                Log.Warning($"User UpdateCheck Fail : {JsonConvert.SerializeObject(x)}");
                return false;
            });

            DeleteCheckActions.Add(x =>
            {
                if (x.IsDeleted == false) return true;
                Log.Warning($"User DeleteCheck Fail : {JsonConvert.SerializeObject(x)}");
                return false;
            });
        }

        //public async Task NotificationUserActive(dynamic data)
        //{
        //    await _notificationManager.Echo(NotificationType.UserActive, data);
        //}

        [UnitOfWork]
        public async Task<int> Register(User user)
        {
            user.IsActive = false;

            int result = await Repository.InsertAndGetIdAsync(user);

            return result;
        }

        [UnitOfWork]
        public Task<User> ActiveUser(int id)
        {
            return Task.Run(() => Repository.Update(id, x => x.IsActive = true));
        }

        [UnitOfWork]
        public Task<User> FindByUserCode(string usercode)
        {
            return Repository.GetIncludingQuery(x => x.SubordinateCompany).FirstOrDefaultAsync(x => x.UserCode.Equals(usercode));
        }

        public string GetPasswordSha256(string password)
        {
            return Encrypt.HmacSHA256(password, AuthorizationServerConsts.DefaultPassPhrase);
        }

        public bool CheckUserPassword(string inputPassword, User user)
        {
            return GetPasswordSha256(inputPassword).Equals(user.CurPassword);
        }

        public Task UpdatePassword(string password, User dbEntity)
        {
            return Task.Run(() =>
            {
                if (!string.IsNullOrEmpty(password))
                {
                    dbEntity.BeforeLastPassword = dbEntity.LastPassword;
                    dbEntity.LastPassword = dbEntity.CurPassword;
                    dbEntity.CurPassword = GetPasswordSha256(password);
                }
            });
        }

        /// <summary>
        /// 管理员修改密码
        /// </summary>
        /// <param name="id"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public Task ResetPasswordForAdmin(int id, string password)
        {
            return Task.Run(async () =>
            {
                if (id < 1)
                    throw new UserFriendlyException("NoUserCanBeChangePassword");

                if (!string.IsNullOrEmpty(password))
                {
                    User dbEntity = Repository.Get(id);
                    await UpdatePassword(password, dbEntity);
                    await Repository.UpdateAsync(dbEntity);
                    await UnitOfWork.SaveChangesAsync();
                }
            });
        }

        /// <summary>
        /// 普通用户修改密码
        /// </summary>
        /// <param name="id"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public Task ResetPassword(int id, string password)
        {
            return Task.Run(async () =>
            {
                if (id < 0)
                    id = Convert.ToInt32(_session.UserId.Value);

                if (!string.IsNullOrEmpty(password))
                {
                    User dbEntity = Repository.Get(id);

                    string passwordSha256 = GetPasswordSha256(password);

                    //旧密码验证
                    if (!dbEntity.CurPassword.Equals(passwordSha256))
                        throw new UserFriendlyException("OriginalPasswordError");

                    //近三次密码不能相同
                    if (dbEntity.CurPassword.Equals(passwordSha256))
                        throw new UserFriendlyException("NearlyThreePasswordsCannotBeTheSame");

                    if (!string.IsNullOrEmpty(dbEntity.LastPassword) && dbEntity.LastPassword.Equals(passwordSha256))
                        throw new UserFriendlyException("NearlyThreePasswordsCannotBeTheSame");

                    if (!string.IsNullOrEmpty(dbEntity.BeforeLastPassword) && dbEntity.BeforeLastPassword.Equals(passwordSha256))
                        throw new UserFriendlyException("NearlyThreePasswordsCannotBeTheSame");

                    await UpdatePassword(password, dbEntity);
                    await Repository.UpdateAsync(dbEntity);
                    await UnitOfWork.SaveChangesAsync();
                }
            });
        }

        public Task<User> UpdateUser(User user)
        {
            return Repository.UpdateAsync(user);
        }

        public User GetUserById(int id)
        {
            return Repository.Get(id);
        }
    }
}